在前面的章節中我們使用了 Hello-World、 Redis 和 Busybox 這三種 image,但這都是別的工程師做好並放在 DockerHub 的 image,而本章節將介紹如何創建屬於你自己的 Image 並說明創建一個自己的 Image 需要經過哪些步驟。

需要創建客製化的 Image 之前,先利用圖表簡單的介紹一下創建一個 image 需要什麼樣的流程。
在圖表中可以看到,創建一個 Image 之前需要先創建一個定義 Container 該有什麼行為的腳本,也稱為 Docker file ,接著就來創建一個 docker file 吧。
首先先創建一個新的資料夾並新增一個檔案,將它稱為 Dockerfile,要注意的是他的第一個字 D 需要大寫並寫這個檔案沒有擴展名,就是單純的 Dockerfile
mkdir redis-image
cd redis-image
touch Dockerfile
接著可以利用隨便一個你喜歡的 IDE 打開 Dockerfile 並添加以下內容
FROM alpine
RUN apk add --update redis
CMD ["redis-server"]
最後移動到存放 Dockerfile 的資料夾中使用 docker build . 將剛剛的 Dockerfile build 起來
當看到 Successfully built xxxxxxxx (id) 代表成功 build 出了 Image,接著可以使用 docker run <imageID> 將剛剛 build 的 image 跑起來看看是否有如預期的 redis-server

剛剛我們建立了一個 Custom 的 Image,在 build image 的過程中會看到很多文字,還有 Dockerfile 中寫的那些是什麼東西,別擔心接著會將 Dockerfile 與 build image 的行為拆開來看
在 Dockerfile 的第一行中寫了 FROM alpine ,其實這個指令代表要建立一個 image 之前,需要先引入基礎的 image,你可以想像當寫一個 Dockerfile 就代表給你一台新的什麼都沒有的電腦,那麼你要在這個電腦中安裝 Chrome 瀏覽器你該怎麼辦?可能會有人說打開預設的瀏覽器然後下載 Chrome,但要注意這台什麼都沒有的電腦就是什麼都沒有,連基本的作業系統都沒有,所以更不可能會有預設的瀏覽器。
而 Dockerfile 的第一行就代表著把這台新的電腦安裝基本的作業系統,安裝預設的瀏覽器,這樣才可以接著下一步做我們需要完成的功能,這就是引入 Base image 的功用。
引入了 Base image 後便可以使用 Base image 中所攜帶的功能,比如我們的範例中使用 Base image 中的 apk add 將 redis 加入到我們的 image 中。
了解了什麼是 Base image 與為何需要他後,接著將介紹在 build image 時 terminal 中的資訊。
在 Terminal 中可以看到三個 Step,他對應著 Docker file 中的三個部分,先從 dockerHub 中載入 base image,接著執行 apk add --update redis 最後執行 CMD 也就是當從這個 image 創建成 container 後預設會執行的指令。
不過你可能會注意到,為什麼從 Step2 開始都會有 Running in xxxxxx (id) 和 Removing intermediate container xxxxxx (id) ?

可以發現 remove intermediate container 和 Running in 的 id 是相同的,代表這是一個 Container ID ,但為什麼有這個 id?
當從 dockerHub 載入 base image 的時候,這時 docker 會為這個行為產生一個 臨時的 image
下一步的行為會基於這個臨時的 image 所創建出來的 container,簡單來說當載入 alpine 這個 base image 時,會臨時的產生一個 image,而 RUN apk add --update redis 這個指令會在這個臨時的 image 所創建的 Container 所執行,因為需要在這個 Container 中才會有可以執行 apk add 這個指令的環境。
與上一步一樣,當第二步利用第一步的臨時 image 的 container 環境執行完指定的程序後,也會生成一個屬於第二部的 image,以畫面上來說就會生成一個 6aa1e9732ef5  image。

和上面一樣,下一步的行為會利用上一部所產生的臨時 image 的 Container 環境來執行指定的程序,所以最後的 CMD ["redis-server"] 就需要在上一步的 image 的 container 中才能執行,因為只有上一步的環境才會有可以執行 redis-server 的環境。
一樣在最後一步結束後也會產生一個 image,但這個 image 就是包含了所有我們指定功能的 image,也就是最後 Successfully built 566572db5631 的這個 image id,最後可以利用這個 id 創建出有我們要求的所有功能的 Container。
上面說明了這麼多,在這裡裡用流程圖簡單的整理一下上面提到的重點。
上面介紹了 build image 的時候會進行什麼操作,最後要來介紹 Docker build 的一個特性,這個特性可以讓我們在 build image 的時候提高效率,他就是 Cache。
Cache 簡單來說就是在 build image 的時候,當 docker 發現這個操作在之前就已經做過了,他們他就會使用 Cache 中的結果而不是將重複的動作再做一次,以我們的例子來說,當我們再次使用 docker build .

首先看到 Step 1,因為我們之前已經 build 過一次 image 了,所以 docker 會偵測到在我們的本機中已經存在 alpine ,所以就不需要再次去 dockerHub 下載下來。
接著 Step 2 中最大的差別在於,他在執行 apk add --update redis 的時後用的是 Using cache,這代表著 docker 偵測到我們對於這個動作已經做過一次了,所以直接使用 Cache 的結果而不是重新在跑一次,這可以大幅度的增加 build image 的效能,就算在 Dockerfile 中新增指令,在 build image 的時候 docker 也會偵測到哪些是已經執行過,就可以將執行過的用 Cache 而專心執行沒執行過指令即可。
FROM alpine
RUN apk add --update redis
RUN apk add --update gcc 
CMD ["redis-server"]

但值得注意的是,如果在 Dockerfile 中更改指令的位置的話,就無法在 build image 的時候使用 Cache,還記得上面介紹的嗎,每一步的執行都需要建立在上一步所建立的臨時 image 的 Container 中,所以對於 docker 來說如果改變了指令的位置,那麼整個過程都會不一樣了,所以在編輯 Dockerfile 的時候要注意對於指令新增更改來說,如果要保持 build image 的效能,那就注意不要更改到指令的順序。
FROM alpine
RUN apk add --update gcc
RUN apk add --update redis
CMD ["redis-server"]

可以看到就算我們剛剛已經 build 過 image 了,但由於指令的位置改變所以就算是以前執行過的指令也都需要在跑一次。
雖然可以利用 docker build . 創建客製化的 image,但是每次 build 出來的都是一個 id,雖然可以使用 docker run <imageID> 來創建 Container,但就不能像之前使用的 docker run hello-world 這樣有一個名稱嗎? 答案是可以的
在 build image 的時候可以加入 -t 這個 option,他的目的是將 build 出來的 image 賦予一個 tag,而可以使用這個 tag 取代掉 build 出來的 image id,而在定義 tag 會有個習慣,通常會是 docker ID / Project Name / version,以上面的例子來說可以寫成 fandix/redis-server:1
